
package com.idealighter.game.games.foursshop.manager;

import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.Operator;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.FoursLogPlanDic;
import com.idealighter.game.dictionary.dic.FoursLogTypeDic;
import com.idealighter.game.dictionary.dic.FoursShopRoomDic;
import com.idealighter.game.dictionary.dic.FoursShopTimeDic;
import com.idealighter.game.dictionary.domain.FoursLogPlanDomain;
import com.idealighter.game.dictionary.domain.FoursLogTypeDomain;
import com.idealighter.game.dictionary.domain.FoursShopRoomDomain;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.foursshop.dto.BetsInfo;
import com.idealighter.game.games.foursshop.struct.FoursShopRoom;
import com.idealighter.game.games.foursshop.struct.FoursShopSeat;
import com.idealighter.game.games.foursshop.struct.FoursShopTable;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game4WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.utils.code.RandCodeUtil;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 4s店 .
 */
@Singleton
public class FoursShopMgr {

  private static final Logger LOG = LoggerFactory.getLogger(FoursShopMgr.class);
  // 网络延时
  private static final int ADDED_DELAY = 0;

  private static final int BANKER_CHIPS = 100000000;
  private static final int REST = 1;
  private static final int BET = 2;
  private static final int REWARD = 3;
  private static final int BALANCE = 4;

  private final EventMgr eventMgr;
  private final FoursShopDataMgr dataMgr;
  private final FoursShopMsgMgr msgMgr;
  private final PlayerMgr playerMgr;
  private final FoursShopRoomDic roomDic;
  private final FoursLogPlanDic foursLogPlanDic;
  private final FoursLogTypeDic foursLogTypeDic;
  private final FoursShopTimeDic timeDic;
  private final ScheduleMgr scheduleMgr;
  private final LoginMgr loginMgr;
  private final CommonMsgMgr commonMsgMgr;
  private FourShopControlScript fourshopControl;

  // 游戏线程executor,可以执行普通任务和定时任务
  public final DisruptorExecutor gameExecutor;

  /**
   * 构造函数.
   * 
   * @param eventMgr .
   * @param executorMgr .
   * @param dataMgr .
   * @param msgMgr .
   * @param playerMgr .
   * @param roomDic .
   * @param foursLogPlanDic .
   * @param foursLogTypeDic .
   * @param timeDic .
   * @param scheduleMgr .
   * @param loginMgr .
   * @param commonMsgMgr .
   */
  @Inject
  public FoursShopMgr(EventMgr eventMgr, ExecutorMgr executorMgr, FoursShopDataMgr dataMgr,
      FoursShopMsgMgr msgMgr, PlayerMgr playerMgr, FoursShopRoomDic roomDic,
      FoursLogPlanDic foursLogPlanDic, FoursLogTypeDic foursLogTypeDic, FoursShopTimeDic timeDic,
      ScheduleMgr scheduleMgr, LoginMgr loginMgr, CommonMsgMgr commonMsgMgr) {
    this.eventMgr = eventMgr;
    this.dataMgr = dataMgr;
    this.msgMgr = msgMgr;
    this.playerMgr = playerMgr;
    this.roomDic = roomDic;
    this.foursLogPlanDic = foursLogPlanDic;
    this.foursLogTypeDic = foursLogTypeDic;
    this.timeDic = timeDic;
    this.scheduleMgr = scheduleMgr;
    this.loginMgr = loginMgr;
    this.commonMsgMgr = commonMsgMgr;
    this.gameExecutor = executorMgr.getGameExecutor(Game.FOURS_SHOP.getModuleId());

    eventMgr.register(this);
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    gameExecutor.execute(() -> {
      Player player = event.player;

      FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
      if (seat != null) {
        // 玩家在房间中退出桌子
        if (seat.table.state == BET && seat.getPlayerId() == seat.table.bankerPlayerId) {
          LOG.info("[4s]庄家[{}][{}]下注阶段退出了");
        }

        doExitTable(player, seat);
      }

      // 玩家是否在4s店房间中
      if (player.curRoom instanceof FoursShopRoom) {
        // 退出房间
        exitRoom(player);
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FOURS_SHOP) {
        roomDic.load();
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FOURS_SHOP) {
        roomDic.load();
        AbstractRoom room = dataMgr.getRoom(event.getRoomId());
        for (Long playerId : room.getPlayers()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            loginMgr.noticeLogoutForGameExit(player);
          }
        }
      }

    });
  }

  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 玩家进入4s店.
   * 
   * @param player 玩家.
   */
  public void enterGameHall(Player player) {
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[4s]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      return;
    }

    msgMgr.sendEnterGameHallMsg(player);
    LOG.info("[4s]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());
  }

  /**
   * 玩家进入房间.
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public void enterRoom(Player player, int roomId) {
    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null) {
      LOG.error("[4s]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getId(), player.getPlayerName(),
          curRoom.game(), curRoom.getName());
      return;
    }

    FoursShopRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.error("[4s]玩家[{}][{}]进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
      return;
    }

    FoursShopRoomDomain roomDom = roomDic.map().get(roomId);
    if (roomDom.getIsActive() != 1) {
      LOG.error("[4s]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
      return;
    }
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getUpper());
      return;
    }

    if (!player.vip()
        && room.getPlayers().size() >= roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_VIP_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(), roomId,
          roomDom.getMaxNum());
      return;
    }

    if (room.getPlayers().size() >= roomDom.getMaxNum()) {
      // msgMgr.sendEnterRoomMsg(player, 1, room);
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(), roomId,
          roomDom.getMaxNum());
      return;
    }

    // 修改玩家当前房间
    player.curRoom = room;
    room.getPlayers().add(player.getId());

    // 发送房间玩家数量变更
    // msgMgr.sendRoomPlayerNumMsg(room);
    // 发送进入房间消息
    msgMgr.sendEnterRoomMsg(player);

    LOG.info("[4s]玩家[{}][{}]进入的房间[{}]成功", player.getPlayerName(), player.getId(), roomId);
    // 触发进入房间事件
    eventMgr.post(new EnterRoomEvent(player, room));
  }

  /**
   * 兑换筹码的金币 . 玩家金币兑换筹码.
   * 
   * @param player 玩家.
   * @param gold .
   */
  public void exchangeChips(Player player, long gold) {
    if (gold < 0) {
      LOG.info("[4s]玩家[{}][{}]兑换筹码金币不能为负数", player.getPlayerName(), player.getId());
      return;
    }
    if (gold > player.getGold()) {
      LOG.info("[4s]玩家[{}][{}]兑换筹码金币超过了自己携带的金币", player.getPlayerName(), player.getId());
      return;
    }
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[4s]玩家[{}][{}]不在牌桌中不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    if (player.getGold() < gold
        + convertGold(seat.table.room.getId().intValue(), seat.getTotalChips())) {
      LOG.error("玩[4s]家[{}][{}]兑换的金币[{}]加上筹码[{}]兑换的金币超过自己的金币总和[{}]", player.getPlayerName(),
          player.getId(), gold, seat.getTotalChips(), player.getGold());
      return;
    }

    if (seat.gaming) {
      LOG.error("[4s]玩家[{}][{}]已经开始游戏不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    // playerMgr.minusGold(player, gold, LogReason.FOURS_SHOP_BILL);
    long chips = convertChip(seat.table.room.getId().intValue(), gold);
    // 增加筹码
    addChips(seat, chips);

    LOG.info("[4s]玩家[{}][{}]用金币[{}]兑换筹码[{}]成功", player.getPlayerName(), player.getId(), gold,
        chips);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 玩家筹码兑换金币.
   * 
   * @param player 玩家.
   * @param chips .
   */
  public void exchangeGold(Player player, long chips) {
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[4s]玩家[{}][{}]不在牌桌中不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }

    if (seat.getTotalChips() < chips) {
      LOG.error("[4s]玩家[{}][{}]兑换的筹码[{}]超过自己的筹码总和[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      return;
    }

    if (seat.gaming) {
      LOG.error("[4s]玩家[{}][{}]已经开始游戏不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getPlayerId() == seat.table.bankerPlayerId) {
      LOG.warn("[4s]玩家[{}][{}]是庄家不允许上下分", player.getPlayerName(), player.getId());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BANKER_NOT_ALLOW_EXCHANGE,
          Operator.SYSTEM);
      return;
    }

    long gold = convertGold(seat.table.room.getId().intValue(), chips);
    // playerMgr.addGold(player, gold, LogReason.FOURS_SHOP_EXCHANGE_GOLD);
    minusChips(seat, chips);

    LOG.info("[4s]玩家[{}][{}]用筹码[{}]兑换金币[{}]成功", player.getPlayerName(), player.getId(), chips,
        gold);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 筹码换算金币.
   * 
   * @param roomId .
   * @param chips .
   */
  public long convertGold(int roomId, long chips) {
    FoursShopRoomDomain roomDomain = roomDic.map().get(roomId);

    return chips * roomDomain.getProportionGold() / roomDomain.getProportionChips();
  }

  /**
   * 金币换算筹码.
   * 
   * @param roomId .
   * @param gold .
   * @return
   */
  private long convertChip(int roomId, long gold) {
    FoursShopRoomDomain roomDomain = roomDic.map().get(roomId);

    return gold * roomDomain.getProportionChips() / roomDomain.getProportionGold();
  }

  /**
   * 玩家退出房间.
   * 
   * @param player 玩家.
   */
  public void exitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());

    if (curRoom instanceof FoursShopRoom) {
      if (seat == null) {
        player.curRoom = null;
        FoursShopRoom room = (FoursShopRoom) curRoom;
        room.getPlayers().remove(player.getId());
        msgMgr.sendExitRoomMsg(player, 0);
        LOG.info("[4s]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
      } else {
        LOG.error("[4s]玩家[{}][{}]在座位中不能退出房间,请先退出桌子", player.getPlayerName(), player.getId());
        msgMgr.sendExitRoomMsg(player, 1);
        return;
      }

      // 触发退出房间事件
      eventMgr.post(new ExitRoomEvent(player, curRoom));
    } else {
      LOG.warn("[4s]玩家[{}][{}]不在房间中,无法退出房间", player.getPlayerName(), player.getId());
    }
  }

  /**
   * 玩家进入桌子.
   * 
   * @param player 玩家.
   * @param tableId .
   */
  public void enterTable(Player player, int roomId, int tableId) {
    if (!(player.curRoom instanceof FoursShopRoom)) {
      LOG.error("[4s]玩家[{}][{}]当前在[{}]房间中不能进入桌子", player.getPlayerName(), player.getId(),
          player.curRoom.game().getDesc());
      return;
    }

    FoursShopTable table = dataMgr.getTable(roomId, tableId);
    if (table == null) {
      LOG.error("[4s]玩家[{}][{}]进入的牌桌[{}]不存在", player.getPlayerName(), player.getId(), tableId);
      return;
    }

    if (dataMgr.getPlayerSeat(player.getId()) != null) {
      LOG.error("[4s]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
      return;
    }
    FoursShopRoomDomain roomDom = roomDic.get(roomId);
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(), roomId,
          roomDom.getUpper());
      return;
    }

    for (FoursShopSeat seat : table.seats) {
      if (seat.getPlayerId() == player.getId()) {
        LOG.error("[4s]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
        return;
      }
    }
    FoursShopSeat playerSeat = null;
    for (FoursShopSeat seat : table.seats) {
      if (seat != null) {
        if (seat.getPlayerId() == 0) {
          playerSeat = seat;
          break;
        }

      }
    }
    if (playerSeat == null) { // 没有位置了
      commonMsgMgr.sendTipDlgMsg(player, ErrorCode.GAME_NOT_SIT, Operator.SYSTEM);
      return;
    }
    doEnterTable(player, playerSeat);
  }

  /**
   * 玩家进入座位,玩家每次进入4s扣台费(金币).
   * 
   * @param player 玩家.
   * @param seat 座位信息.
   */
  private void doEnterTable(Player player, FoursShopSeat seat) {
    final FoursShopTable table = seat.table;
    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setSex(player.getSex());
    seat.setState(SeatState.GAMING);
    seat.setRobot(player.playerBo().isRobot());
    // FoursShopRoomDomain roomDom = roomDic.get(roomId);
    // if (player.getGold() < roomDom.getLower()) {
    // commonMsgMgr.sendErrorDlgMsg(player, 32, 0);
    // LOG.warn("玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(),
    // player.getId(), roomId, roomDom.getLower());
    // return;
    // }
    //
    // if (player.getGold() > roomDom.getUpper()) {
    // commonMsgMgr.sendErrorDlgMsg(player, 33, 0);
    // LOG.warn("玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(),
    // player.getId(), roomId, roomDom.getUpper());
    // return;
    // }

    // 台费(金币)
    int afee = roomDic.map().get(table.room.getId().intValue()).getAfee();
    if (player.getGold() < afee) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_ENOUGH_FEE, Operator.SYSTEM);
      LOG.warn("[4s]玩家[{}][{}]进入桌子金币不足台费", player.getPlayerName(), player.getId());
      return;
    } else {
      playerMgr.minusGold(player, afee, LogReason.FOURS_SHOP_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, table, afee));
    }

    // 更新玩家座位
    dataMgr.updatePlayerSeats(player.getId(), seat);
    // 玩家进入桌子想发送本局倍率
    msgMgr.sendFoursShopIcons(player, table.plans);
    // 触发进入桌子事件
    eventMgr.post(new EnterTableEvent(player, seat));
    // 向玩家发送进入牌桌结果信息
    msgMgr.sendEnterTableMsg(player, table);
    // 向同房间的玩家广播房间位置变化信息
    // msgMgr.sendSeatInfoUpdateMsg(seat);
    // 发送庄家信息给玩家
    msgMgr.sendBankerInfo(player, table);
    // 发送庄家排队列表
    msgMgr.sendBankerList(player, table);

    msgMgr.sendCardResult(player, table);

    // 告知玩家当前的阶段和倒计时时间
    msgMgr.sendTimeToEnterPlayer(table, player);

    // 当前的下注信息
    if (table.state == BET) {
      msgMgr.sendBetsChange(table, player);
    }
    msgMgr.sendMosaicGold(player, table.mosaicGold);
    // table.playerNum += 1;
    LOG.info("[4s]玩家[{}][{}]进入桌子[{}]成功", player.getPlayerName(), player.getId(), table.getId());
  }

  /**
   * 玩家退出牌桌.
   * 
   * @param player 玩家.
   */
  public void exitTable(Player player) {
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[4s]玩家[{}][{}]没在牌桌中不能退出牌桌", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getPlayerId() == seat.table.bankerPlayerId && seat.table.state == 2) {
      LOG.error("[4s]庄家[{}][{}]在下注的阶段退出了", player.getPlayerName(), player.getId());
    }

    doExitTable(player, seat);
  }

  /**
   * 退出桌子.
   * 
   * @param seat 座位信息.
   */
  private void doExitTable(Player player, FoursShopSeat seat) {
    if (seat.table.state == BET || seat.table.state == REWARD) {
      // minusChips(seat, seat.table.getAllBets());
      if (seat.table.totalBet != 0 && seat.getPlayerId() == seat.table.bankerPlayerId) {
        billGold(seat);
      }
    }
    // doBill(seat);
    if (seat.getPlayerId() == seat.table.bankerPlayerId) { // 庄家在上庄的时候退出(包括强退)
      seat.table.isCancelBanker = true;
      if (seat.table.state == REST) {
        doChangeBanker(seat.table);
      } else {
        seat.isbanker = false;
        seat.table.bankerPlayerId = 0;
      }
    }
    if (seat.table.bankers.contains(seat.getPlayerId())) { // 玩家在申请队列中退出(包括强退)
      seat.table.bankers.remove(seat.getPlayerId());
      msgMgr.sendcancleBanker(null, seat.table, 0);
    }
    // 发送退出房间消息
    msgMgr.sendExitTableMsg(seat);
    dataMgr.removePlayerSeat(seat.getPlayerId());
    // seat.table.room.players.remove(seat.playerId);

    LOG.info("[4s]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.table.getId());
    // 向同房间的玩家广播房间位置变化信息
    // msgMgr.sendSeatInfoUpdateMsg(seat);
    seat.clear();
    clearSeat(seat.table, player);
    // 触发退出桌子事件
    eventMgr.post(new ExitTableEvent(player, seat));
  }

  /**
   * 清除座位信息.
   *
   * @param table .
   * @param player 玩家.
   */
  private void clearSeat(FoursShopTable table, Player player) {
    for (FoursShopSeat seat : table.seats) {
      if (seat.getPlayerId() == player.getId()) {
        seat.clear();
      }
    }
  }

  /**
   * 增加筹码.
   * 
   * @param seat 座位信息.
   * @param chips .
   */
  private void addChips(FoursShopSeat seat, long chips) {
    seat.setTotalChips(seat.getTotalChips() + chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
  }

  /**
   * 减少筹码.
   * 
   * @param seat 座位信息.
   * @param chips .
   */
  private void minusChips(FoursShopSeat seat, long chips) {
    seat.setTotalChips(seat.getTotalChips() - chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
  }

  /**
   * 获取本次游戏的所有图标倍率.
   *
   * @param roomId .
   * @param tableId .
   */
  public void randomRate(int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    if (table == null) {
      return;
    }
    List<FoursLogPlanDomain> plans = Lists.newArrayList();
    int planId = randomPlan();
    plans.addAll(foursLogPlanDic.list().stream().filter(foursPlan -> foursPlan != null)
        .filter(foursPlan -> foursPlan.getType() == planId).collect(Collectors.toList()));
    table.plans = plans;
    msgMgr.sendrandomFoursShopIcons(table, plans);
  }

  /**
   * 该局所开出的结果 . 倍率，图标.
   *
   * @param table .
   */
  public void randomRewardResult(FoursShopTable table) {
    long earn = 0;
    int win = fourshopControl.controlReward(table);
    for (;;) {
      randCar(table);
      if (table.bankerPlayerId != 0) {
        if (table.mosaicGoldCount == 2) {
          if (table.tempcarId == table.getCarId()) {
            continue;
          }
        }
        earn = table.getRobotTotalBetInfo() - table.getRobotTotal();
      } else {
        if (table.mosaicGoldCount >= 2 && table.tempcarId == table.getCarId()) {
          earn = table.getPlayerTotal() - table.getPlayerTotalBetInfo() * 2;
        } else {
          earn = table.getPlayerTotal() - table.getPlayerTotalBetInfo();
        }
      }
      if (earn == 0) {
        break;
      }
      if (win == 3) {
        break;
      }
      if (win == 0 && earn > 0) {
        break;
      }
      if (win == 1 && earn < 0) {
        break;
      }
    }
    if (table.getCarId() == table.tempcarId) {
      table.mosaicGoldCount += 1;
    } else {
      table.mosaicGoldCount = 0;
    }
    table.earn = earn;
    table.setCarId(table.tempcarId);
    table.setRate(table.temprate);
    table.startIndex = RandCodeUtil.random(1, 32);
    table.rewardTime = RandCodeUtil.random(7500, 8500);
    msgMgr.sendsRandCarResult(table, table.getCarId(), table.getRate(), table.startIndex,
        table.rewardTime);
  }

  private void randCar(FoursShopTable table) {
    int random = RandCodeUtil.random(foursLogPlanDic.getTotalWeight());
    int sum = 0;
    for (FoursLogPlanDomain plan : table.plans) {
      sum += plan.getProbability();
      if (random < sum) {
        table.tempcarId = plan.getCarid();
        table.temprate = plan.getRate();
        break;
      }
    }
  }

  /**
   * 根据配置随机配置中一种方案.
   * 
   * @return
   */
  private int randomPlan() {
    int rand = RandCodeUtil.random(foursLogTypeDic.getTotalWeight());
    int sum = 0;
    for (FoursLogTypeDomain typeDomain : foursLogTypeDic.list()) {
      sum += typeDomain.getPlanProbability();
      if (rand < sum) {
        return typeDomain.getPlanId();
      }
    }
    return 1;
  }

  /**
   * 检测庄家和庄家申请列表中的玩家身上所携带筹码是否足够.
   *
   * @param table .
   */
  public void checkBanckerChips(FoursShopTable table) {
    if (table.bankerPlayerId != 0) {
      FoursShopSeat bankerSeat = dataMgr.getPlayerSeats().get(table.bankerPlayerId);
      if (bankerSeat == null) {
        LOG.info("庄家[{}]不存在", table.bankerPlayerId);
        return;
      }
      if (bankerSeat.getTotalChips() < BANKER_CHIPS) {
        doChangeBanker(table);
        return;
      } else {
        Queue<Long> temp = Queues.newLinkedBlockingQueue(3);
        temp.addAll(table.bankers);
        if (!temp.isEmpty()) {
          for (Object nextBanker : temp) {
            if (nextBanker instanceof Long) {
              bankerSeat = dataMgr.getPlayerSeats().get(nextBanker);
              if (bankerSeat == null) {
                table.bankers.remove(nextBanker);
                continue;
              }
              if (bankerSeat.getTotalChips() < BANKER_CHIPS) {
                table.bankers.remove(nextBanker);
                continue;
              }
            }
          }
        }
        // msgMgr.sendBankerInfo(null, table);
        // msgMgr.sendOnBanker(null, 0, table);
      }
    }
  }

  /**
   * 玩家申请上庄 . ->请求进入队列,在休息阶段且队列里面没人的情况下可以直接上庄.
   *
   * @param player 玩家.
   */
  public void onBanker(Player player, int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.warn("[4s]玩家[{}][{}]上庄房间[{}]错误", player.getPlayerName(), player.getId(), roomId);
      return;
    }
    FoursShopTable table = room.tables.get(tableId);
    if (table == null) {
      LOG.warn("[4s]玩家[{}][{}]上庄桌子[{}]错误", player.getPlayerName(), player.getId(), tableId);
      return;
    }
    if (player.getId() == table.bankerPlayerId) {
      LOG.warn("[4s]玩家[{}][{}]已经是庄家，不能重复申请", player.getPlayerName(), player.getId());
      return;
    }
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.warn("[4s]玩家[{}][{}]没有在店", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getTotalChips() < BANKER_CHIPS) {
      LOG.info("[4s]玩家[{}][{}]申请失败，玩家所携的筹码数不足一千万", player.getPlayerName(), player.getId());
      // msgMgr.sendOnBanker(player, 1, table);
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_CHIP_NOT_ENOUGH, Operator.SYSTEM,
          BANKER_CHIPS + "");
      return;
    }
    if (table.bankers.contains(player.getId())) {
      msgMgr.sendOnBanker(player, 1, table);
      LOG.info("[4s]玩家[{}][{}]已经在庄家队列中,不能重复申请", player.getPlayerName(), player.getId());
      return;
    }
    if (table.bankers.size() >= 3) {
      LOG.warn("[4s]玩家[{}][{}]申请失败，已达上庄最大数量", player.getPlayerName(), player.getId());
      msgMgr.sendOnBanker(player, 1, table);
      return;
    }
    if (table.bankers.isEmpty() && table.state == REST) { // 系统坐庄，该阶段直接上庄
      if (table.bankerPlayerId == 0) {
        table.bankerPlayerId = player.getId();
        seat.isbanker = true;
        table.score = 0;
        table.count = 0;
        msgMgr.sendBankerInfo(null, table);
        LOG.info("[4s]玩家[{}][{}]申请上庄成功", player.getPlayerName(), player.getId());
        return;
      }
    }
    table.bankers.add(player.getId());
    msgMgr.sendOnBanker(player, 0, table);
    LOG.info("[4s]玩家[{}][{}]申请成功", player.getPlayerName(), player.getId());
  }

  /**
   * 执行上庄操作.
   *
   * @param table .
   */
  public void doOnbanker(FoursShopTable table) {
    if (table.bankers.isEmpty()) {
      return;
    }
    long bankerId = table.bankers.remove();
    FoursShopSeat bankerSeat = dataMgr.getPlayerSeats().get(bankerId);
    if (bankerSeat == null) {
      LOG.info("[4s]庄家[{}]已经不在游戏中", bankerId);
      return;
    }
    bankerSeat.isbanker = true;
    table.bankerPlayerId = bankerId;
    table.score = 0;
    table.count = 0;
    msgMgr.sendBankerInfo(null, table);
    msgMgr.sendOnBanker(null, 0, table);
  }

  /**
   * 玩家取消上庄排队 . ->(庄家队列里面的玩家) .
   */
  public void cancleBanker(Player player, int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    if (!table.bankers.contains(player.getId())) {
      msgMgr.sendcancleBanker(player, table, 1);
      LOG.info("[4s]取消上庄 失败，玩家[{}][{}]并未处于队列中", player.getPlayerName(), player.getId());
      return;
    }
    LOG.info("[4s]玩家[{}][{}]取消上庄成功", player.getPlayerName(), player.getId());
    table.bankers.remove(player.getId());
    msgMgr.sendcancleBanker(player, table, 0);
  }

  /**
   * 玩家申请下庄 . (休息阶段申请可以直接下庄,否则到下个休息阶段才下庄) .
   */
  public void cancleOnBanker(Player player, int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.warn("[4s]玩家[{}][{}]申请下庄 房间[{}]错误", player.getPlayerName(), player.getId(), roomId);
      return;
    }
    FoursShopTable table = room.tables.get(tableId);
    if (table == null) {
      LOG.warn("[4s]玩家[{}][{}]申请下庄 桌子[{}]错误", player.getPlayerName(), player.getId(), tableId);
      return;
    }
    if (table.bankerPlayerId != player.getId()) {
      msgMgr.sendCancelBanker(player, 1);
      LOG.info("[4s]玩家[{}][{}]不是庄家不能申请下庄", player.getPlayerName(), player.getId());
      return;
    }
    if (table.state == REST) { // 休息阶段直接下庄
      doChangeBanker(table);
      LOG.info("[4s]玩家[{}][{}]休息阶段申请了下庄", player.getPlayerName(), player.getId());
    } else { // 非休息阶段等这一句结束到下一个休息阶段下庄
      table.isCancelBanker = true;
      msgMgr.sendCancelBanker(player, 0);
      LOG.info("[4s]玩家[{}][{}]申请下庄成功", player.getPlayerName(), player.getId());
    }
  }

  /**
   * 庄家变更.
   *
   * @param table .
   */
  public void doChangeBanker(FoursShopTable table) {
    if (table.bankerPlayerId == 0) {
      if (table.bankers.isEmpty()) {
        table.score = 0;
        table.count = 0;
        msgMgr.sendBankerInfo(null, table);
        return;
      }
    }
    FoursShopSeat bankerSeat = dataMgr.getPlayerSeats().get(table.bankerPlayerId); // ->取消原庄家,指定新庄家
    if (bankerSeat == null) {
      LOG.info("[4s]庄家变更庄家[{}]不存在", table.bankerPlayerId);
      return;
    }
    bankerSeat.isbanker = false;
    Queue<Long> temp = Queues.newLinkedBlockingQueue(3);
    temp.addAll(table.bankers);
    if (!temp.isEmpty()) {
      for (Object nextBanker : temp) {
        if (nextBanker instanceof Long) {
          bankerSeat = dataMgr.getPlayerSeats().get(nextBanker);
          if (bankerSeat == null) {
            table.bankers.remove(nextBanker);
            continue;
          }
          if (bankerSeat.getTotalChips() > BANKER_CHIPS) {
            table.bankerPlayerId = (Long) nextBanker;
            bankerSeat.isbanker = true;
            table.bankers.remove(nextBanker);
            break;
          }
        }
      }
    } else {
      table.bankerPlayerId = 0;
    }
    table.score = 0;
    table.count = 0;
    msgMgr.sendBankerInfo(null, table);
    msgMgr.sendOnBanker(null, 0, table);
  }

  /**
   * 玩家下注.
   *
   * @param player 玩家.
   * @param bets .
   */
  public void doBet(Player player, int roomId, int tableId, BetsInfo bets) {
    if (bets == null || player == null) {
      LOG.info("[4s]玩家[{}][{}]下注失败", player.getId(), player.getUserName());
      msgMgr.sendDoBet(player, 1);
      return;
    }
    FoursShopRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.info("[4s]房间不存在");
      return;
    }
    FoursShopTable table = room.tables.get(1);
    if (table.state != BET) {
      LOG.info("[4s]玩家[{}][{}]下注失败，状态不对", player.getName(), player.getUserName());
      return;
    }
    if (player.getId() == table.bankerPlayerId) {
      LOG.info("[4s]玩家[{}][{}]下注失败,你是庄家不能下注", player.getId(), player.getUserName());
      msgMgr.sendDoBet(player, 1);
      return;
    }
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.info("[4s]玩家[{}][{}]已经不在桌子中", player.getId(), player.getUserName());
      return;
    }
    long billBet = bets.getBet();
    if (billBet < 0) {
      LOG.info("[4s]玩家[{}][{}]下注失败，下注筹码错误，不能为小于0", player.getId(), player.getUserName());
      return;
    }
    if (seat.getTotalChips() < billBet) {
      LOG.info("[4s]玩家[{}][{}]下注失败，筹码不足", player.getId(), player.getUserName());
      msgMgr.sendDoBet(player, 1);
      return;
    }
    if (table.bankerPlayerId != 0) { // 庄家为玩家
      long tempTotal =
          (table.getTotalBet(bets.getCarId()) + bets.getBet()) * table.getRate(bets.getCarId());

      FoursShopSeat bankerSeat = dataMgr.getPlayerSeats().get(table.bankerPlayerId);
      if (bankerSeat == null) {
        LOG.info("[4s]庄家[{}]已经不在桌子中", table.bankerPlayerId);
        return;
      }
      if (tempTotal > bankerSeat.getTotalChips()) {
        LOG.info("[4s]玩家[{}][{}]下注失败,庄家[{}]筹码不够", player.getId(), player.getUserName(),
            table.bankerPlayerId);
        msgMgr.sendDoBet(player, 1);
        return;
      }
    }
    if (table.betsTotal.containsKey(bets.getCarId())) {
      long tep = table.betsTotal.get(bets.getCarId()) + bets.getBet();
      table.betsTotal.put(bets.getCarId(), tep);
    } else {
      table.betsTotal.put(bets.getCarId(), bets.getBet());
    }
    if (seat.isRobot()) {
      if (table.robotBetsTotal.containsKey(bets.getCarId())) {
        long tep = table.robotBetsTotal.get(bets.getCarId()) + bets.getBet();
        table.robotBetsTotal.put(bets.getCarId(), tep);
      } else {
        table.robotBetsTotal.put(bets.getCarId(), bets.getBet());
      }
      table.totalRobotBet += billBet;
    } else {
      if (table.playerBetsTotal.containsKey(bets.getCarId())) {
        long tep = table.playerBetsTotal.get(bets.getCarId()) + bets.getBet();
        table.playerBetsTotal.put(bets.getCarId(), tep);
      } else {
        table.playerBetsTotal.put(bets.getCarId(), bets.getBet());
      }
      table.totalplayerBet += billBet;
    }
    table.totalBet += billBet;
    minusChips(seat, billBet);
    goldChange(player, seat, -billBet);
    if (!seat.getBets().containsKey(bets.getCarId())) {
      seat.getBets().put(bets.getCarId(), bets);
    } else {
      BetsInfo temp = seat.getBets().get(bets.getCarId());
      temp.setBet(temp.getBet() + bets.getBet());
    }
    msgMgr.sendDoBet(player, 0);
  }


  /**
   * 下注 .
   * 
   * @param table .
   */
  public void doBet(FoursShopTable table) {
    checkBanckerChips(table);
    table.state = BET;
    int betTime = timeDic.map().get(2).getTime();
    msgMgr.sendTime(table, table.state, betTime);
    table.stepFuture = scheduleMgr.schedule(() -> doReward(table), betTime + ADDED_DELAY,
        TimeUnit.SECONDS, gameExecutor);
    table.stepFuturePush =
        scheduleMgr.scheduleWithFixedDelay(() -> msgMgr.sendBetsChange(table, null), betTime / 4,
            betTime / 4, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[4s]房间[{}]桌子[{}]阶段[{}][{}]", table.room.getId(), table.getId(), table.state, "请下注");
  }

  /**
   * 续压.
   *
   * @param player 玩家.
   * @param roomId .
   * @param tableId .
   */
  public void continueBet(Player player, int roomId, int tableId) {
    // bets.stream().filter(betsInfo -> betsInfo != null).forEach(betsInfo
    // -> doBet(player, roomId, tableId, betsInfo));
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    int billBet = 0;
    for (BetsInfo betsInfo : seat.prebets.values()) {
      billBet += betsInfo.getBet();
    }
    if (seat.getTotalChips() < billBet) {
      LOG.info("[4s]玩家[{}][{}]续押失败,筹码不足", player.getId(), player.getUserName());
      msgMgr.sendContineBet(player, 1);
      return;
    }
    // long tempTotal = table.totalBet + billBet;
    if (table.bankerPlayerId != 0) { // 庄家为玩家
      FoursShopSeat bankerSeat = dataMgr.getPlayerSeats().get(table.bankerPlayerId);
      for (BetsInfo betsInfo : seat.prebets.values()) {
        long bet = betsInfo.getBet() + table.betsTotal.getOrDefault(betsInfo.getCarId(), 0L);
        if (bet * table.getRate(betsInfo.getCarId()) > bankerSeat.getTotalChips()) {
          LOG.info("[4s]玩家[{}][{}]续押失败,庄家[{}]筹码不足", player.getId(), player.getUserName(),
              table.bankerPlayerId);
          msgMgr.sendContineBet(player, 1);
          return;
        }
      }
    }
    table.totalBet += billBet;
    for (BetsInfo betsInfo : seat.prebets.values()) {
      if (table.betsTotal.containsKey(betsInfo.getCarId())) {
        long tep = table.betsTotal.get(betsInfo.getCarId()) + betsInfo.getBet();
        table.betsTotal.put(betsInfo.getCarId(), tep);
      } else {
        table.betsTotal.put(betsInfo.getCarId(), betsInfo.getBet());
      }
      if (!seat.getBets().containsKey(betsInfo.getCarId())) {
        BetsInfo betsInfo1 = new BetsInfo();
        betsInfo1.setCarId(betsInfo.getCarId());
        betsInfo1.setBet(betsInfo.getBet());
        seat.getBets().put(betsInfo.getCarId(), betsInfo1);
      } else {
        BetsInfo temp = seat.getBets().get(betsInfo.getCarId());
        temp.setBet(temp.getBet() + betsInfo.getBet());
      }
      if (!seat.isRobot()) {
        if (table.playerBetsTotal.containsKey(betsInfo.getCarId())) {
          long tep = table.playerBetsTotal.get(betsInfo.getCarId()) + betsInfo.getBet();
          table.playerBetsTotal.put(betsInfo.getCarId(), tep);
        } else {
          table.playerBetsTotal.put(betsInfo.getCarId(), betsInfo.getBet());
        }
      }
    }
    msgMgr.sendContineBet(player, 0);
    minusChips(seat, billBet);
    goldChange(player, seat, -billBet);
  }

  /**
   * 清空下注.
   *
   * @param player 玩家.
   * @param roomId .
   * @param tableId .
   */
  public void clearBet(Player player, int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    if (table.state != BET) {
      LOG.info("[4s]玩家[{}][{}]清空下注失败,只能在下注阶段才能清空下注", player.getId(), player.getUserName());
      msgMgr.sendClearBet(player, 1);
      return;

    }
    FoursShopSeat seat = dataMgr.getPlayerSeat(player.getId());
    long betTotal = 0;
    for (BetsInfo betsInfo : seat.getBets().values()) {
      if (betsInfo != null) {
        if (table.betsTotal.containsKey(betsInfo.getCarId())) {
          table.betsTotal.put(betsInfo.getCarId(),
              table.betsTotal.get(betsInfo.getCarId()) - betsInfo.getBet());
        }
        if (!seat.isRobot()) {
          if (table.playerBetsTotal.containsKey(betsInfo.getCarId())) {
            table.playerBetsTotal.put(betsInfo.getCarId(),
                table.playerBetsTotal.get(betsInfo.getCarId()) - betsInfo.getBet());
          }
        }
        betTotal += betsInfo.getBet();
      }
    }
    table.totalBet -= betTotal;
    addChips(seat, betTotal);
    goldChange(player, seat, betTotal);
    seat.getBets().clear();
    msgMgr.sendClearBet(player, 0);
  }

  /**
   * 结算(开奖阶段就发消息了).
   *
   * @param tableId . 桌号
   */
  public void doBalance(int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    long total = 0; // 所有闲家总收益
    long totalBet = table.totalBet;
    long mosicBet = 0;
    for (FoursShopSeat seat : table.seats) { // 闲家收益
      if (seat != null && seat.getPlayerId() > 0 && seat.getPlayerId() != table.bankerPlayerId) {
        for (BetsInfo betsInfo : seat.bets.values()) {
          if (betsInfo != null) {
            seat.bet += betsInfo.getBet(); // 单个玩家总共下注的筹码
            if (betsInfo.getCarId() == table.getCarId()) { // 压中
              long bet = betsInfo.getBet();
              seat.billChips += bet * table.getRate();
              if (table.mosaicGoldCount >= 3) { // 满足彩金条件
                seat.mosaci = seat.billChips;
                mosicBet += seat.mosaci;
              }
            }
          }
        }
        total += seat.billChips;
        seat.setTotalChips(seat.getTotalChips() + seat.billChips);
        seat.billChips += seat.mosaci;
        if (seat.getPlayerId() > 0) {
          LOG.info(
              "[4s]Table CarId [{}] ,Table Rate[{}], Players TotalBet[{}], Players TotalWin[{}]",
              table.getCarId(), table.getRate(), totalBet, total);
        }
      }
    }
    long bankerTotalChips = 0;
    if (table.bankerPlayerId != 0) { // 庄家收益(玩家)
      FoursShopSeat bankerseat = dataMgr.getPlayerSeat(table.bankerPlayerId);
      if (bankerseat == null) {
        LOG.info("[4s]庄家[{}]已经不在桌子中", table.bankerPlayerId);
        return;
      }
      bankerseat.billChips = totalBet - total;
      bankerseat.setTotalChips(bankerseat.getTotalChips() + bankerseat.billChips);
      bankerTotalChips = bankerseat.getTotalChips();

    } else { // 系统收益
      if (totalBet > total) {
        table.mosaicGold += totalBet - total;
      }
      if (table.mosaicGoldCount >= 3) {
        table.mosaicGold = table.mosaicGold - mosicBet > 0 ? table.mosaicGold - mosicBet : 0;
      }
    }
    table.count += 1;
    table.score += totalBet - total;
    LOG.info("[4s]桌子[{}]庄家赢得筹码:[{}]", tableId, String.valueOf(totalBet - total));
    msgMgr.sendBalance(table, totalBet - total, bankerTotalChips);

    for (FoursShopSeat seat : table.seats) {
      if (seat.getPlayerId() == 0) {
        continue;
      }
      // 触发房间大厅成员信息变更事件
      eventMgr.post(new RoomMemInfoUpdateEvent(playerMgr.getPlayer(seat.getPlayerId())));
    }
    fourshopControl.balanceControl(table);
  }

  /**
   * 结算 .
   */
  public void doBalance(FoursShopTable table) {
    billGold(table);
    table.state = BALANCE;
    int balanceTime = timeDic.map().get(4).getTime();
    msgMgr.sendTime(table, table.state, balanceTime);
    table.stepFuture =
        scheduleMgr.schedule(() -> doRest(table.room.getId().intValue(), table.getId().intValue()),
            balanceTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);
    LOG.info("[4s]房间[{}]桌子[{}]阶段[{}][{}]", table.room.getId(), table.getId(), table.state, "结算中");
  }

  /**
   * 结算金币.
   *
   * @param table .
   */
  private void billGold(FoursShopTable table) {
    int roomId = table.room.getId().intValue();
    for (FoursShopSeat seat : table.seats) {
      if (seat.getPlayerId() > 0) {
        // long chips = seat.billBet > 0 ? seat.billBet : -seat.bet;
        // if (seat.robot) {//why???
        // continue;
        // }
        long chips = seat.billChips;
        long gold = convertGold(roomId, chips);

        Player player = playerMgr.getPlayer(seat.getPlayerId());
        PlayerBo playerDom = playerMgr.selectPlayer(seat.getPlayerId());
        if (gold != 0) {
          if (player != null) {
            playerMgr.addGold(player, gold, LogReason.FOURS_SHOP_BILL);
          } else {
            playerMgr.addGold(playerDom, gold, LogReason.FOURS_SHOP_BILL);
          }
        }
        DbLogService
            .log(new Game4WinLoseLog(playerDom, seat, chips, gold, LogReason.FOURS_SHOP_BILL));
      }
    }
  }

  /**
   * 结算金币.
   *
   * @param seat 座位信息.
   */

  private void billGold(FoursShopSeat seat) {
    long chips;
    if (seat.getPlayerId() == seat.table.bankerPlayerId) {
      chips = -seat.table.getAllBets();
    } else {
      chips = -seat.bet;
    }
    long gold = convertGold(seat.table.room.getId().intValue(), chips);
    Player player = playerMgr.getPlayer(seat.getPlayerId());
    PlayerBo playerDom = playerMgr.selectPlayer(seat.getPlayerId());
    if (player != null) {
      playerMgr.addGold(player, gold, LogReason.FOURS_SHOP_BILL);
    } else {
      playerMgr.addGold(playerDom, gold, LogReason.FOURS_SHOP_BILL);
    }
    DbLogService.log(new Game4WinLoseLog(playerDom, seat, chips, gold, LogReason.FOURS_SHOP_BILL));
  }

  /**
   * 玩家下注金币变化.
   *
   * @param player 玩家.
   * @param seat 座位信息.
   * @param bet .
   */
  private void goldChange(Player player, FoursShopSeat seat, long bet) {
    long gold = convertGold(seat.table.room.getId().intValue(), bet);
    playerMgr.addGold(player, gold, LogReason.FOURS_SHOP_BET);
    DbLogService.log(new Game4WinLoseLog(player, seat, bet, gold, LogReason.FOURS_SHOP_BET));
  }

  /**
   * 游戏开始 .
   */
  @Inject
  public void start(FourShopControlScript fourshopControl) {
    LOG.info("[4s]游戏开始");
    this.fourshopControl = fourshopControl;
    for (FoursShopRoom room : dataMgr.rooms()) {
      for (FoursShopTable table : room.tables.values()) {
        doRest(room.getId().intValue(), table.getId().intValue());
      }
    }
  }

  /**
   * 休息 .
   */
  public void doRest(int roomId, int tableId) {
    FoursShopRoom room = dataMgr.getRoom(roomId);
    FoursShopTable table = room.tables.get(tableId);
    // table.betsChange.clear(); //清除上一桌的下注信息
    // sendMsgToClient() ->开始计时

    // 单独通知机器人下注区域

    msgMgr.sendRobtBetCar(table);
    table.seats.stream().forEach(seat -> {
      if (seat.getPlayerId() == 0) {
        seat.setTotalChips(0);
      }
      if (seat.getPlayerId() > 0 && seat.billChips > 0) {
        msgMgr.sendedNotice(seat.getPlayerName(), table.getCarId(), seat.billChips, seat.mosaci);
      }
      seat.getPrebets();
      seat.getBets().clear();
      seat.bet = 0;
      seat.billChips = 0;
      seat.mosaci = 0;
    });
    // table.betsInfos.clear();
    if (table.isCancelBanker) {
      doChangeBanker(table);
    }
    if (table.bankerPlayerId == 0) {
      doOnbanker(table);
    }

    checkBanckerChips(table);
    table.clear();

    int readyTime = timeDic.map().get(1).getTime();
    // 休息到指定时间到下注阶段
    table.state = REST;
    msgMgr.sendTime(table, table.state, readyTime);
    randomRate(roomId, tableId);
    LOG.info("[4s]房间[{}]桌子[{}]阶段[{}][{}]", roomId, tableId, table.state, "请休息");
    table.stepFuture = scheduleMgr.schedule(() -> doBet(table), readyTime + ADDED_DELAY,
        TimeUnit.SECONDS, gameExecutor);
  }



  /**
   * 开奖 .
   */
  public void doReward(FoursShopTable table) {
    msgMgr.sendBetsChange(table, null);
    table.stepFuturePush.cancel(false);
    table.state = REWARD;
    randomRewardResult(table);
    int rewardTime = timeDic.map().get(3).getTime();
    msgMgr.sendTime(table, table.state, rewardTime);
    doBalance(table.room.getId().intValue(), table.getId().intValue());
    table.stepFuture = scheduleMgr.schedule(() -> doBalance(table), rewardTime + ADDED_DELAY,
        TimeUnit.SECONDS, gameExecutor);
    LOG.info("[4s]房间[{}]桌子[{}]阶段[{}][{}]", table.room.getId(), table.getId(), table.state, "开奖中");
  }



  /**
   * 玩家在游戏中卡住，游戏无法正常进行下去，GM操作强制清除该桌数据.
   * 
   * @param playerId 玩家id.
   */
  private void doClearGameData(long playerId) {
    try {
      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (FoursShopRoom room : dataMgr.rooms()) {
        room.getPlayers().remove(playerId);
        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (FoursShopTable table : room.tables.values()) {
          table.bankers.remove(playerId);
          if (table.bankerPlayerId == playerId) {
            table.bankerPlayerId = 0;
          }
          for (FoursShopSeat seat : table.seats) {
            if (seat.getPlayerId() == playerId) {
              seat.clear();
              Player player = playerMgr.getPlayer(playerId);
              if (player != null) {
                loginMgr.noticeLogoutForGameExit(player);
              }
            }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[4s]清除玩家[" + playerId + "]卡限失败", e);
    }
  }

}
